import re
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

# 设置matplotlib以正确显示中文字符
plt.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体为黑体
plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题


def parse_backtest_log_with_duration(file_path):
    """
    解析回测日志文件，提取开仓指标、最终盈亏以及持仓时间。

    Args:
        file_path (str): 日志文件的路径。

    Returns:
        pandas.DataFrame: 包含分析所需列的数据框。
    """
    try:
        with open(file_path, 'r', encoding='UTF-8') as f:
            content = f.read()
    except FileNotFoundError:
        print(f"错误：找不到文件 {file_path}")
        return None
    except Exception as e:
        print(f"读取文件时出错: {e}")
        return None

    blocks = content.split('=========\n')

    open_positions = {}
    completed_trades = []

    # 更新后的正则表达式模式
    id_pattern = re.compile(r"├─ 仓位ID : ([\w-]+)")
    time_pattern = re.compile(r"├─ 交易时间: ([\dT:+ -]+)\[")
    reason_pattern = re.compile(
        r"波动率: ([\d.]+), .*volaZScore\(([\d.-]+)\).*volumeZScore\(([\d.-]+)\)"
    )
    pnl_pattern = re.compile(r"├─ 本笔盈亏: ([-\d.]+)")

    for block in blocks:
        id_match = id_pattern.search(block)
        if not id_match:
            continue

        position_id = id_match.group(1)
        time_match = time_pattern.search(block)
        if not time_match:
            continue
        trade_time = pd.to_datetime(time_match.group(1))

        if "[开仓做" in block:
            reason_match = reason_pattern.search(block)
            if reason_match:
                open_positions[position_id] = {
                    'volatility': float(reason_match.group(1)),
                    'volaZScore': float(reason_match.group(2)),
                    'volumeZScore': float(reason_match.group(3)),
                    'open_time': trade_time
                }

        elif "[平仓" in block or "[止损" in block:
            pnl_match = pnl_pattern.search(block)
            if pnl_match and position_id in open_positions:
                pnl = float(pnl_match.group(1))
                trade_data = open_positions.pop(position_id)

                trade_data['pnl'] = pnl
                # 计算持仓时间（小时）
                duration = (trade_time - trade_data['open_time']).total_seconds() / 3600
                trade_data['duration_hours'] = duration

                del trade_data['open_time']  # 移除临时时间戳
                completed_trades.append(trade_data)

    if not completed_trades:
        print("警告：未能从日志中解析出任何完整的交易。")
        return pd.DataFrame()

    return pd.DataFrame(completed_trades)


def analyze_and_visualize_with_duration(df):
    """
    分析波动率、Z-Scores、持仓时间与盈亏的关系并进行可视化。

    Args:
        df (pandas.DataFrame): 包含分析所需数据的数据框。
    """
    if df.empty:
        return

    # 1. 描述性统计
    print("--- 描述性统计 ---")
    print(df.describe())

    # 2. 计算相关系数矩阵
    print("\n--- 相关系数矩阵 ---")
    correlation_matrix = df.corr()
    print(correlation_matrix)

    print("\n--- 与盈亏(pnl)的相关性排序 ---")
    pnl_corr = correlation_matrix['pnl'].sort_values(ascending=False)
    print(pnl_corr)

    # 3. 按盈利和亏损分组进行统计
    df['盈利情况'] = np.where(df['pnl'] > 0, '盈利', '亏损')
    print("\n--- 按盈利/亏损分组统计 ---")
    grouped_stats = df.groupby('盈利情况').agg({
        'duration_hours': ['mean', 'median', 'std'],
        'volaZScore': ['mean', 'median'],
        'volumeZScore': ['mean', 'median'],
        'pnl': ['mean', 'count']
    })
    print(grouped_stats)

    # 4. 可视化分析

    # 图1: 相关系数热力图
    plt.figure(figsize=(10, 8))
    sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', fmt=".2f", linewidths=.5)
    plt.title('特征相关性热力图', fontsize=16)
    plt.show()

    # 创建一个4x2的图形网格用于深度分析
    fig, axes = plt.subplots(4, 2, figsize=(16, 22))
    fig.suptitle('开仓指标与交易盈亏关系深度分析', fontsize=20, y=1.02)

    metrics = [
        ('volatility', '绝对波动率'),
        ('volaZScore', '波动率Z-Score'),
        ('volumeZScore', '成交量Z-Score'),
        ('duration_hours', '持仓小时数')
    ]

    for i, (metric, title) in enumerate(metrics):
        # 左侧子图: 散点图与回归线
        sns.regplot(data=df, x=metric, y='pnl', ax=axes[i, 0],
                    scatter_kws={'alpha': 0.5}, line_kws={"color": "red"})
        axes[i, 0].set_title(f'{title} vs. 盈亏 (带回归线)')
        axes[i, 0].set_xlabel(f'{title}')
        axes[i, 0].set_ylabel('单笔盈亏 (PNL)')
        axes[i, 0].grid(True)

        # 右侧子图: 按盈利/亏损分类的箱形图
        sns.boxplot(data=df, x='盈利情况', y=metric, ax=axes[i, 1])
        axes[i, 1].set_title(f'盈利与亏损交易的{title}分布')
        axes[i, 1].set_xlabel('交易结果')
        axes[i, 1].set_ylabel(f'{title}')

    plt.tight_layout()
    plt.show()


# --- 主程序 ---
if __name__ == "__main__":
    file_path = '回测结果 (2).txt'
    trades_df_final = parse_backtest_log_with_duration(file_path)

    if trades_df_final is not None and not trades_df_final.empty:
        analyze_and_visualize_with_duration(trades_df_final)
    else:
        print("未能加载或解析数据，分析终止。")